package com.xnyzc.lhy.common.util.retry;

import com.alibaba.fastjson.JSONObject;
import com.xnyzc.lhy.common.component.didi.DiDiUrlConstant;
import com.xnyzc.lhy.common.constant.RetryConstant;
import com.xnyzc.lhy.common.penum.EDiDiErrorCode;
import com.xnyzc.lhy.common.util.CheckUtil;
import com.xnyzc.lhy.common.util.RequestMethodUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

import java.time.LocalTime;

/**
 * 接口重试
 * @author zhengxu
 */
@Slf4j
public class RetryUtil {

    /**
     * didi专用接口重试方法
     * @param requestParam 参数
     * @param url 地址
     * @param maxAttempts 最大重试次数
     * @param millisecond 重试时间
     * @param coefficient 系数
     * @throws Exception
     */
    public static String retryRequestMethodUtil(String requestParam, String url, Integer maxAttempts, Long millisecond, Double coefficient) throws Exception {
        RetryTemplate retryTemplate = new RetryTemplate();

        SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();

        if (CheckUtil.objIsEmpty(maxAttempts) || maxAttempts < RetryConstant.NUM_1 || maxAttempts > RetryConstant.NUM_6) {
            maxAttempts = RetryConstant.MAX_ATTEMPTS;
        }
        simpleRetryPolicy.setMaxAttempts(maxAttempts);

        ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
        if (CheckUtil.objIsEmpty(millisecond) || millisecond < 0L || millisecond > RetryConstant.NUM_5000L) {
            millisecond = RetryConstant.MILLISECOND;
        }
        exponentialBackOffPolicy.setInitialInterval(millisecond);

        if (CheckUtil.objIsEmpty(coefficient) || coefficient < RetryConstant.NUM_1 || coefficient > RetryConstant.NUM_6) {
            coefficient = RetryConstant.COEFFICIENT;
        }
        exponentialBackOffPolicy.setMultiplier(coefficient);

        retryTemplate.setRetryPolicy(simpleRetryPolicy);
        retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);

        String result = retryTemplate.execute(new RetryCallback<String, Exception>() {
            int i = 0;

            // 重试操作
            @Override
            public String doWithRetry(RetryContext retryContext) throws Exception {
                if (retryContext.getRetryCount() == 0) {
                    log.info(LocalTime.now() + "请求滴滴接口，地址为：{}，参数为：{}", url, requestParam);
                } else {
                    log.info(LocalTime.now() + "请求滴滴接口失败！！触发重试机制，第 {} 次重试请求，地址为：{}，参数为：{}", retryContext.getRetryCount(), url, requestParam);
                }
                String data = RequestMethodUtil.requestMethod(requestParam, url);
                log.info("请求滴滴返回数据为{}", data);
                if (CheckUtil.strIsEmpty(data)) {
                    throw new Exception();
                }
                JSONObject jsonObject = JSONObject.parseObject(data);
                if (!jsonObject.get(DiDiUrlConstant.ERRNO).equals(EDiDiErrorCode.success.getValue())) {
                    throw new Exception();
                }
                return data;
            }

        }, new RecoveryCallback<String>() {
            // 兜底操作
            @Override
            public String recover(RetryContext retryContext) throws Exception {
                log.info("重试 {} 次后，调用了兜底方法！", retryContext.getRetryCount() - 1);
                return null;
            }
        });

        return result;
    }

    /**
     * didi工单专用接口重试方法
     * @param requestParam 参数
     * @param url 地址
     * @param maxAttempts 最大重试次数
     * @param millisecond 重试时间
     * @param coefficient 系数
     * @throws Exception
     */
    public static String requestMethodJson(net.sf.json.JSONObject requestParam, String url, Integer maxAttempts, Long millisecond, Double coefficient) throws Exception {
        RetryTemplate retryTemplate = new RetryTemplate();

        SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();

        if (CheckUtil.objIsEmpty(maxAttempts) || maxAttempts < RetryConstant.NUM_1 || maxAttempts > RetryConstant.NUM_6) {
            maxAttempts = RetryConstant.MAX_ATTEMPTS;
        }
        simpleRetryPolicy.setMaxAttempts(maxAttempts);

        ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
        if (CheckUtil.objIsEmpty(millisecond) || millisecond < 0L || millisecond > RetryConstant.NUM_5000L) {
            millisecond = RetryConstant.MILLISECOND;
        }
        exponentialBackOffPolicy.setInitialInterval(millisecond);

        if (CheckUtil.objIsEmpty(coefficient) || coefficient < RetryConstant.NUM_1 || coefficient > RetryConstant.NUM_6) {
            coefficient = RetryConstant.COEFFICIENT;
        }
        exponentialBackOffPolicy.setMultiplier(coefficient);

        retryTemplate.setRetryPolicy(simpleRetryPolicy);
        retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);

        String result = retryTemplate.execute(new RetryCallback<String, Exception>() {
            int i = 0;

            // 重试操作
            @Override
            public String doWithRetry(RetryContext retryContext) throws Exception {
                if (retryContext.getRetryCount() == 0) {
                    log.info(LocalTime.now() + "请求滴滴接口，地址为：{}，参数为：{}", url, requestParam);
                } else {
                    log.info(LocalTime.now() + "请求滴滴接口失败！！触发重试机制，第 {} 次重试请求，地址为：{}，参数为：{}", retryContext.getRetryCount(), url, requestParam);
                }
                String data = RequestMethodUtil.requestMethodJson(requestParam, url);
                log.info("请求滴滴返回数据为{}", data);
                if (CheckUtil.strIsEmpty(data)) {
                    throw new Exception();
                }
                JSONObject jsonObject = JSONObject.parseObject(data);
                if (!jsonObject.get(DiDiUrlConstant.ERRNO).equals(EDiDiErrorCode.success.getValue())) {
                    throw new Exception();
                }
                return data;
            }

        }, new RecoveryCallback<String>() {
            // 兜底操作
            @Override
            public String recover(RetryContext retryContext) throws Exception {
                log.info("重试 {} 次后，调用了兜底方法！", retryContext.getRetryCount() - 1);
                return null;
            }
        });

        return result;
    }
}
